package com.hx.zhg.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateUtilExt {

    public static enum Format {
        YYYY("yyyy"),
        YYYYMM("yyyyMM"),
        MM("MM"),
        HHMM("HH:mm"),
        MMDD("MMdd"),
        MMDD_POINT("MM.dd"),
        MM_DD("MM-dd"),
        YYYYMMEEE("yyyyMMEEE"),
        YYYYMMDD("yyyyMMdd"),
        YYMMDDHH("yyMMddHH"),
        YYYYMMDDHH("yyyyMMddHH"),
        YYYYMMDDHHMM("yyyyMMddHHmm"),
        YYYYMMDDHHMMSS("yyyyMMddHHmmss"),
        HYPHEN_YYYYMM("yyyy-MM"),
        HYPHEN_YYYYMMEEE("yyyy-MM-EEE"),
        HYPHEN_YYYYMMDD("yyyy-MM-dd"),
        HYPHEN_YYYYMMDDHH("yyyy-MM-dd HH"),
        HYPHEN_YYYYMMDDHHMM("yyyy-MM-dd HH:mm"),
        HYPHEN_YYYYMMDDHHMMSS("yyyy-MM-dd HH:mm:ss"),
        SLANT_YYYYMM("yyyy/MM"),
        SLANT_YYYYMMEEE("yyyy/MM/EEE"),
        SLANT_YYYYMMDD("yyyy/MM/dd"),
        SLANT_YYYYMMDDHH("yyyy/MM/dd HH"),
        SLANT_YYYYMMDDHHMM("yyyy/MM/dd HH:mm"),
        SLANT_YYYYMMDDHHMMSS("yyyy/MM/dd hh:mm:ss"),
        HYPHEN_POINT_YYYYMMDDHHMM("yyyy.MM.dd HH:mm");

        private String format;

        private Format(String format) {

            this.format = format;
        }

        public String getFormat() {

            return format;
        }

        public int getType() {

            String[] names = name().split("_");
            String name = null;
            if (names.length > 1) {
                name = names[1];
            } else {
                name = names[0];
            }
            String pref = ".*";
            if (name.matches(pref + "Y+$")) {
                return Calendar.YEAR;
            } else if (name.matches(pref + "M+$")) {
                return Calendar.MONTH;
            } else if (name.matches(pref + "E+$")) {
                return Calendar.WEEK_OF_YEAR;
            } else if (name.matches(pref + "D+$")) {
                return Calendar.DATE;
            } else if (name.matches(pref + "H+$")) {
                return Calendar.HOUR;
            } else if (name.matches(pref + "H+M+$")) {
                return Calendar.MINUTE;
            } else if (name.matches(pref + "S+$")) {
                return Calendar.SECOND;
            } else {
                throw new IllegalArgumentException("Illegal Date Type");
            }
        }
    }

    public static int getFormatType(String pattern) {

        for (Format format : Format.values()) {
            if (format.getFormat().equals(pattern)) {
                return format.getType();
            }
        }
        return -1;
    }

    /**
     * return current date
     *
     * @return current date
     */
    public static Date getCurrentDate() {

        return new Date(System.currentTimeMillis());
    }

    public static Date add(Date date, int amount, int field) {

        if (field == Calendar.YEAR) {
            return DateUtils.addYears(date, amount);
        } else if (field == Calendar.MONTH) {
            return DateUtils.addMonths(date, amount);
        } else if (field == Calendar.DATE || field == Calendar.DAY_OF_MONTH) {
            return DateUtils.addDays(date, amount);
        } else if (field == Calendar.WEEK_OF_MONTH || field == Calendar.WEEK_OF_YEAR) {
            return DateUtils.addWeeks(date, amount);
        } else if (field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY) {
            return DateUtils.addHours(date, amount);
        } else if (field == Calendar.SECOND) {
            return DateUtils.addSeconds(date, amount);
        } else if (field == Calendar.MINUTE) {
            return DateUtils.addMinutes(date, amount);
        } else {
            throw new IllegalArgumentException("Un support date type : " + field);
        }
    }

    public static long diff(Date left, Date right, TimeUnit unit) {

        return unit.convert(left.getTime() - right.getTime(), TimeUnit.MILLISECONDS);
    }

    public static long diff(Date left, long delay, Date right, TimeUnit unit) {

        return unit.convert(left.getTime() + TimeUnit.MILLISECONDS.convert(delay, unit) - right.getTime(), TimeUnit.MILLISECONDS);
    }

    public static Date getStarting(Date date, int field) {

        if (field == Calendar.YEAR) {
            return DateUtils.truncate(date, Calendar.YEAR);
        } else if (field == Calendar.MONTH) {
            return DateUtils.truncate(date, Calendar.MONTH);
        } else if (field == Calendar.DATE || field == Calendar.DAY_OF_MONTH) {
            return DateUtils.truncate(date, Calendar.DATE);
        } else if (field == Calendar.WEEK_OF_MONTH || field == Calendar.WEEK_OF_YEAR) {
            Calendar cal = Calendar.getInstance(Locale.CHINA);
            cal.setFirstDayOfWeek(Calendar.MONDAY);
            cal.setTime(DateUtils.truncate(date, Calendar.DATE));
            cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            return cal.getTime();
        } else if (field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY) {
            return DateUtils.truncate(date, Calendar.HOUR);
        } else {
            throw new IllegalArgumentException("Un support date type : " + field);
        }
    }

    public static Date getEnding(Date date, int field) {

        if (field == Calendar.YEAR) {
            return DateUtils.addYears(DateUtils.truncate(date, Calendar.YEAR), 1);
        } else if (field == Calendar.MONTH) {
            return DateUtils.addMonths(DateUtils.truncate(date, Calendar.MONTH), 1);
        } else if (field == Calendar.DATE || field == Calendar.DAY_OF_MONTH) {
            return DateUtils.addDays(DateUtils.truncate(date, Calendar.DATE), 1);
        } else if (field == Calendar.WEEK_OF_MONTH || field == Calendar.WEEK_OF_YEAR) {
            Calendar cal = Calendar.getInstance(Locale.CHINA);
            cal.setFirstDayOfWeek(Calendar.MONDAY);
            cal.setTime(DateUtils.truncate(date, Calendar.DATE));
            cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            return DateUtils.addWeeks(cal.getTime(), 1);
        } else if (field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY) {
            return DateUtils.addHours(DateUtils.truncate(date, Calendar.HOUR), 1);
        } else {
            throw new IllegalArgumentException("Un support date type : " + field);
        }
    }

    public static boolean isEqual(Date left, Date right, int... fields) {

        Calendar cLeft = Calendar.getInstance();
        cLeft.setTime(left);
        Calendar cRight = Calendar.getInstance();
        cRight.setTime(right);
        for (int field : fields) {
            if (cLeft.get(field) != cRight.get(field)) {
                return false;
            }
        }
        return true;
    }

    /**
     * format the date in given pattern
     *
     * @param d
     * @return the formatted string
     */
    public static String formatDate(Date d) {

        if (d == null) {
            throw new IllegalArgumentException("given date is invalid " + d);
        }
        return formatDate(d, Format.HYPHEN_YYYYMMDD);
    }

    /**
     * format the date in given pattern
     *
     * @param d       date
     * @param pattern time pattern
     * @return the formatted string
     */
    public static String formatDate(Date d, String pattern) {

        if (null == d || StringUtils.isBlank(pattern)) {
            throw new IllegalArgumentException("given parameters is invalid " + d + " - " + pattern);
        }
        SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance();
        formatter.applyPattern(pattern);
        return formatter.format(d);
    }

    /**
     * format the date in given pattern
     *
     * @param d       date
     * @param pattern time pattern
     * @return the formatted string
     */
    public static String formatDate(Date d, Format pattern) {

        if (null == d || pattern == null) {
            throw new IllegalArgumentException("given date or pattern is invalid " + d + " - " + pattern);
        }
        SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance();
        formatter.applyPattern(pattern.getFormat());
        return formatter.format(d);
    }

    /**
     * change string to date
     *
     * @param sDate the date string
     * @return Date object
     */
    public static Date formatDate(String sDate) {

        Date dt = null;
        if (StringUtils.isNotBlank(sDate)) {
            for (Format format : Format.values()) {
                try {
                    dt = DateUtils.parseDateStrictly(sDate, format.getFormat());
                } catch (ParseException e) {
                    dt = null;
                    continue;
                }
                break;
            }
        }
        if (dt == null) {
            throw new IllegalArgumentException("given pattern is invalid " + sDate);
        }
        return dt;
    }

    /**
     * change string to date
     *
     * @param sDate  the date string
     * @param format the date format
     * @return Date object
     */
    public static Date formatDate(String sDate, Format format) {

        Date dt = null;
        try {
            dt = DateUtils.parseDateStrictly(sDate, format.getFormat());
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
        return dt;
    }

    public static Date truncate(Date date, Format format) {

        return DateUtils.truncate(date, format.getType());
    }

    public static String getWeekOfDate(Date date) {

        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) w = 0;
        return weekDays[w];
    }

    public static String getWeekOfDate2(Date date) {

        String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) w = 0;
        return weekDays[w];
    }

    /**
     * @param str
     * @return
     * @Description:去掉日期格式里面的-或者/，比如：2014-07-06转换为20140706
     */
    public static String formatDateString(String str) {
        Pattern p = Pattern.compile("(\\d+)");
        Matcher m = p.matcher(str);
        StringBuilder dateStr = new StringBuilder();
        while (m.find()) {
            dateStr.append(m.group());
        }
        return dateStr.toString();
    }

    /**
     * 得到N月前月初
     *
     * @param n
     * @return
     */
    public static Calendar getLastNMonthFirstDay(int n) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.MONTH, -n);
        return calendar;
    }

    /**
     * 得到N月前月末
     *
     * @param n
     * @return
     */
    public static Calendar getLastNMonthEndDay(int n) {
        Calendar calendar = getLastNMonthFirstDay(n);
        int monthDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.add(Calendar.DAY_OF_YEAR, monthDays - 1);
        return calendar;
    }

    /**
     * 得到N周前周一
     *
     * @param n
     * @return
     */
    public static Calendar getLastNtWeekMonday(int n) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.WEEK_OF_YEAR, -n);
        if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
            calendar.add(Calendar.DAY_OF_YEAR, -6);
        } else {
            calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        }
        return calendar;
    }

    /**
     * 得到N周前周日
     *
     * @param n
     * @return
     */
    public static Calendar getLastNtWeekSunday(int n) {
        Calendar calendar = getLastNtWeekMonday(n);
        calendar.add(Calendar.DAY_OF_YEAR, 6);
        return calendar;
    }

    /**
     * 根据用户生日计算年龄
     */
    public static int getAgeByBirthday(Date birthday) {
        Calendar cal = Calendar.getInstance();
        Calendar birthCal = Calendar.getInstance();
        birthCal.setTime(birthday);
        if (cal.before(birthCal)) {
            throw new IllegalArgumentException(
                    "The birthDay is before Now.It's unbelievable!");
        }
        int yearNow = cal.get(Calendar.YEAR);
        int monthNow = cal.get(Calendar.MONTH) + 1;
        int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
        cal.setTime(birthday);
        int yearBirth = cal.get(Calendar.YEAR);
        int monthBirth = cal.get(Calendar.MONTH) + 1;
        int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
        int age = yearNow - yearBirth;
        if (monthNow <= monthBirth) {
            if (monthNow == monthBirth) {
                // monthNow==monthBirth
                if (dayOfMonthNow < dayOfMonthBirth) {
                    age--;
                }
            } else {
                // monthNow>monthBirth
                age--;
            }
        }
        return age;
    }

    /**
     * 大于当前时间返回默认值0
     *
     * @param birthCal
     * @return
     */
    public static Map<String, Integer> getAgeWithMonthDay(Calendar birthCal) {
        Calendar cal = Calendar.getInstance();
        Map<String, Integer> map = new HashMap<>();
        int age = 0;
        int month = 0;
        if (cal.before(birthCal)) {
            map.put("age", age);
            map.put("month", month);
            return map;
        }
        int yearNow = cal.get(Calendar.YEAR);
        int monthNow = cal.get(Calendar.MONTH) + 1;
        int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
        int yearBirth = birthCal.get(Calendar.YEAR);
        int monthBirth = birthCal.get(Calendar.MONTH) + 1;
        int dayOfMonthBirth = birthCal.get(Calendar.DAY_OF_MONTH);
        age = yearNow - yearBirth;
        if (monthNow <= monthBirth) {
            if (monthNow == monthBirth) {
                if (dayOfMonthNow < dayOfMonthBirth) {
                    age--;
                    month = 12;
                } else {
                    month = 0;
                }
            } else {
                age--;
                month = 12 - monthBirth + monthNow;
            }
        } else {
            month = monthNow - monthBirth;
        }
        map.put("age", age);
        map.put("month", month);
        return map;
    }


    /**
     * 得到今天以前的N天
     *
     * @param days
     * @return
     */
    public static List<Date> getBeforeDays(Date startDate, Integer days) {
        if (startDate == null || days == null || days <= 0) {
            throw new IllegalArgumentException(
                    "The days is invalid");
        }
        List<Date> resultList = new ArrayList<>();
        Instant nowInstant = startDate.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(nowInstant, zone);
        LocalDate now = localDateTime.toLocalDate();
        for (int i = 0; i < days; i++) {
            LocalDate ld = now.minusDays(i);
            Instant instant = ld.atStartOfDay().atZone(zone).toInstant();
            Date date = Date.from(instant);
            resultList.add(date);
        }
        return resultList;
    }

    /**
     * 根据用户生日计算年龄
     *
     * @param birthday 8位 例如:19921025
     * @return
     */
    public static int getAgeByIntBirthday(Integer birthday) {
        if (birthday == null || birthday <= 0) {
            throw new IllegalArgumentException("The birthday is invalid");
        }
        String birthday_str = String.valueOf(birthday);
        if (birthday_str.length() != 8) {
            throw new IllegalArgumentException("The birthday length is invalid");
        }
        int birthday_year = Integer.parseInt(birthday_str.substring(0, 4));
        Calendar cal = Calendar.getInstance();
        cal.setTime(DateUtilExt.getCurrentDate());
        int curr_year = cal.get(Calendar.YEAR);
        return curr_year - birthday_year;
    }

    /**
     * 根据时区获取时间
     *
     * @param timeZone
     * @return
     * @throws Exception
     */
    public static Date getByTimeZone(TimeZone timeZone) throws Exception {
        if (timeZone == null) {
            timeZone = TimeZone.getDefault();
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Format.HYPHEN_YYYYMMDDHHMMSS.format);
        simpleDateFormat.setTimeZone(timeZone);
        SimpleDateFormat sdf = new SimpleDateFormat(Format.HYPHEN_YYYYMMDDHHMMSS.format);
        return sdf.parse(simpleDateFormat.format(new Date()));
    }

    /**
     * 根据时区的id获取时间
     *
     * @param timeZoneId
     * @return
     * @throws Exception
     */
    public static Date getByTimeZoneId(String timeZoneId) throws Exception {
        TimeZone timeZone;
        if (StringUtils.isBlank(timeZoneId)) {
            timeZone = TimeZone.getDefault();
        } else {
            timeZone = TimeZone.getTimeZone(timeZoneId);
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Format.HYPHEN_YYYYMMDDHHMMSS.format);
        simpleDateFormat.setTimeZone(timeZone);
        SimpleDateFormat sdf = new SimpleDateFormat(Format.HYPHEN_YYYYMMDDHHMMSS.format);
        return sdf.parse(simpleDateFormat.format(new Date()));
    }

    /**
     * 根据日期查询星座
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static String getConstellation(Date date) throws Exception {
        if (date == null) {
            return "";
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        String constellation;
        switch (month) {
            case 1:
                constellation = day <= 19 ? "摩羯座" : "水瓶座";
                break;
            case 2:
                constellation = day <=18 ? "水瓶座" : "双鱼座";
                break;
            case 3:
                constellation = day <= 20 ? "双鱼座" : "白羊座";
                break;
            case 4:
                constellation = day <= 19 ? "白羊座" : "金牛座";
                break;
            case 5:
                constellation = day <= 20 ? "金牛座" : "双子座";
                break;
            case 6:
                constellation = day <= 21 ? "双子座" : "巨蟹座";
                break;
            case 7:
                constellation = day <= 22 ? "巨蟹座" : "狮子座";
                break;
            case 8:
                constellation = day <= 22 ? "狮子座" : "处女座";
                break;
            case 9:
                constellation = day <= 22 ? "处女座" : "天秤座";
                break;
            case 10:
                constellation = day <= 23 ? "天秤座" : "天蝎座";
                break;
            case 11:
                constellation = day <= 22 ? "天蝎座" : "射手座";
                break;
            case 12:
                constellation = day <= 21 ? "射手座" : "摩羯座";
                break;
            default:
                constellation = "";
                break;
        }
        return constellation;

    }

    public static void main(String[] args)throws Exception {
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.MONTH,9);
        instance.set(Calendar.DAY_OF_MONTH,1);
        System.out.println(instance.getTime());
        String constellation = getConstellation(instance.getTime());
        System.out.println(constellation);
    }


//	/**
//	 *计算日期差多少天
//	 * @param startDate
//	 * @param endDate
//	 * @return
//     */
//	public static int getDiffDataForDay(String startDate,String endDate) {
//		if (startDate == null || startDate.length() <= 0) {
//			throw new IllegalArgumentException("The startDate is invalid");
//		}
//		if (endDate == null || endDate.length() <= 0) {
//			throw new IllegalArgumentException("The endDate is invalid");
//		}
//		Integer start_year = null;
//		Integer end_year = null;
//		{
//			String[] split = startDate.split("[ ]");
//			String s = split[0];
//			String[] split1 = s.split("[-]");
//			start_year = Integer.parseInt(split1[2]);
//		}
//		{
//			String[] split = endDate.split("[ ]");
//			String s = split[0];
//			String[] split1 = s.split("[-]");
//			end_year = Integer.parseInt(split1[2]);
//		}
//		return end_year - start_year;
//	}

}
